package cn.mw.cmdb.service.handler.monitorData;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.mw.cmdb.entity.ItemDropInfo;
import cn.mw.cmdb.enums.DateTypeEnum;
import cn.mw.cmdb.enums.HistoryTypeEnum;
import cn.mw.cmdb.enums.ZabbixItemUnitEnum;
import cn.mw.cmdb.param.ItemNameParam;
import cn.mw.cmdb.param.MwCoordinateDataInfo;
import cn.mw.cmdb.param.MwItemHistoryResult;
import cn.mw.cmdb.param.QueryItemHistoryParam;
import cn.mw.cmdb.param.zabbixResult.MWItemHistoryDTO;
import cn.mw.cmdb.param.zabbixResult.MwMonitorHistoryParam;
import cn.mw.microMonitorCommon.entity.UnitResult;
import cn.mw.microMonitorCommon.util.DateUtils;
import cn.mw.microMonitorCommon.util.UnitUtil;
import com.alibaba.nacos.common.utils.CollectionUtils;
import com.google.common.base.Strings;
import lombok.extern.slf4j.Slf4j;

import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import static cn.mw.cmdb.util.BaseUtil.getTimeIntervalByType;
import static cn.mw.cmdb.util.ValConvertUtil.*;
import static cn.mw.microMonitorCommon.util.ListMapObjUtils.objectsToMaps;

@Slf4j
public class MonitorHandlerUtils {
    private static final String avgType = "value_avg";
    private static final String minType = "value_min";
    private static final String maxType = "value_max";
    private static final int defaultDelay = 120;

    public static List<MwItemHistoryResult> monitorConvertDataHandle(List<MWItemHistoryDTO> monitorHistoryData, MwMonitorHistoryParam param,
                                                                     Map<String, ItemDropInfo> itemInfoByMap) {
        Map<String, List<MWItemHistoryDTO>> collect = monitorHistoryData.stream().collect(Collectors.groupingBy(MWItemHistoryDTO::getItemid));
        List<MwItemHistoryResult> historyResultList = new ArrayList<>();
        for (Map.Entry<String, ItemDropInfo> entry : itemInfoByMap.entrySet()) {
            ItemDropInfo itemDropInfo = entry.getValue();
            int valType = intValueConvert(itemDropInfo.getValue_type());
            itemDropInfo.itemIdsConvert();
            List<String> itemids = itemDropInfo.getItemids();
            //此处需要根据itemInfoByMap的key值判断，数据获取是否拆分devName，
            //key为itemDropInfo的name，表示合并处理，itemIds为原始全部的，合并为一组，求取极值
            //key为item，表示拆分处理，每条数据单独显示，不需要合并
            if (CollectionUtils.isNotEmpty(itemids) && itemids.contains(entry.getKey())) {
                //不拆分，单独处理
                itemids = new ArrayList<>();
                itemids.add(entry.getKey());
            }
            //此itemids为一组，进行合并处理
            List<List<MWItemHistoryDTO>> allDataDTO = new ArrayList<>();
            for (String itemId : itemids) {
                if (collect != null && collect.containsKey(itemId)) {
                    List<MWItemHistoryDTO> mwItemHistoryDTOS = collect.get(itemId);
                    allDataDTO.add(mwItemHistoryDTOS);
                }
            }
            ParamContext paramContext = new ParamContext();
            if (CollectionUtils.isNotEmpty(allDataDTO) && allDataDTO.size() > 0) {
                //数据合并处理
                List<MWItemHistoryDTO> histortList = mergeListData(allDataDTO, valType);
                MwItemHistoryResult historyResult = new MwItemHistoryResult();
                historyResult.setTitleName(Strings.isNullOrEmpty(itemDropInfo.getChName())? itemDropInfo.getName():itemDropInfo.getChName());
                boolean isNumber = false;
                if (valType == 0 || valType == 3) {
                    isNumber = true;
                }
                MWItemHistoryDTO lastDTO = histortList.get(histortList.size() - 1);
                double maxValue = 0;
                DecimalFormat decimalFormat = new DecimalFormat(UnitUtil.patternKey);
                //监控数据基础单位
                String baseUnits = itemDropInfo.getUnits();
                if (isNumber) {
                    MWItemHistoryDTO maxValueDto = monitorHistoryData.stream().max(Comparator.comparing(s -> doubleValueConvert(s.getValue()))).get();
                    maxValue = doubleValueConvert(maxValueDto.getValue());
                    String value = lastDTO.getValue();
                    //如果是趋势，取平均值数据
                    if (HistoryTypeEnum.Trend.getCode().equals(param.getTrendType())){
                        value = lastDTO.getValue_avg();
                    }
                    historyResult.setLastValue(decimalFormat.format(new BigDecimal(doubleValueConvert(value)) ));
                    UnitResult LastConvertedValue = UnitUtil.getConvertedValue(new BigDecimal(doubleValueConvert(value)), baseUnits);
                    historyResult.setLastUnit(LastConvertedValue.getUnits());
                }else{
                    String value = lastDTO.getValue();
                    //如果是趋势，取平均值数据
                    if (HistoryTypeEnum.Trend.getCode().equals(param.getTrendType() )){
                        value = lastDTO.getValue_avg();
                    }
                    historyResult.setLastValue(value);
                }
                //最新数据进行单位转换
                UnitResult maxConvertedValue = UnitUtil.getConvertedValue(new BigDecimal(maxValue), baseUnits);
                historyResult.setLastUpdateTime(lastDTO.getDateConvert());
                historyResult.setDelay(itemDropInfo.getDelay());
                historyResult.setType(itemDropInfo.getDevName());
                paramContext.setConvertUnit(maxConvertedValue.getUnits());
                paramContext.setDelay(itemDropInfo.getDelay());
                paramContext.setDateType(param.getDateType());
                paramContext.setBaseUnit(baseUnits);
                paramContext.setListDto(histortList);
                MwCoordinateDataInfo avgDto = new MwCoordinateDataInfo();
                MwCoordinateDataInfo maxDto = new MwCoordinateDataInfo();
                MwCoordinateDataInfo minDto = new MwCoordinateDataInfo();
                if (!isNumber) {
                    avgDto = setDateListByType(histortList);
                } else {
                    //是否趋势数据
                    if (HistoryTypeEnum.Trend.getCode().equals(param.getTrendType())) {
                        avgDto = setDateList(histortList, avgType, baseUnits);
                        maxDto = setDateList(histortList, maxType, baseUnits);
                        minDto = setDateList(histortList, minType, baseUnits);
                    } else {
                        paramContext.setExtremumType(avgType);
                        avgDto = extremumHandle(paramContext);
                        paramContext.setExtremumType(maxType);
                        maxDto = extremumHandle(paramContext);
                        paramContext.setExtremumType(minType);
                        minDto = extremumHandle(paramContext);
                    }
                }
                historyResult.setAvgData(avgDto);
                historyResult.setMaxData(maxDto);
                historyResult.setMinData(minDto);
                historyResultList.add(historyResult);
            }
        }
        return historyResultList;
    }

    private static List<MWItemHistoryDTO> mergeListData(List<List<MWItemHistoryDTO>> allDataDTO, int valType) {
        if (allDataDTO.size() == 1) {
            return allDataDTO.get(0);
        }
        //获取最大的集合size
        int maxListSize = allDataDTO.stream().mapToInt(List::size).max().orElse(0);
        double[] countSums = new double[maxListSize];
        //基础数据
        List<MWItemHistoryDTO> baseList = new ArrayList<>();
        //Value_type=0,表示浮点数字，3表示整形数值
        if ((valType == 0 || valType == 3) && maxListSize > 0) {
            for (List<MWItemHistoryDTO> dtoList : allDataDTO) {
                //集合最大size，小于size的，表示有缺失数据，忽略。
                if (CollectionUtils.isNotEmpty(dtoList) && dtoList.size() == maxListSize) {
                    for (int i = 0; i < dtoList.size(); i++) {
                        MWItemHistoryDTO dto = dtoList.get(i);
                        countSums[i] += doubleValueConvert(dto.getValue());
                    }
                    baseList = dtoList;
                }
            }
            for (int i = 0; i < baseList.size(); i++) {
                double avgVal = countSums[i] / allDataDTO.size();
                Object val;
                if (valType == 3) {
                    val = (long) Math.ceil(avgVal);
                } else {
                    val = String.format("%.2f", avgVal);
                }
                baseList.get(i).setValue(strValueConvert(val));
            }
        }
        return baseList;
    }

    public static QueryItemHistoryParam historyParamConvert(MwMonitorHistoryParam param) {
        QueryItemHistoryParam historyParam = new QueryItemHistoryParam();
        BeanUtil.copyProperties(param, historyParam, CopyOptions.create().setIgnoreNullValue(true));
        String startDateStr = param.getStartDate();
        String endDateStr = param.getEndDate();
        List<Long> list = getTimeIntervalByType(startDateStr, endDateStr, param.getDateType());
        Long startTime = DateUtils.addHours(new Date(), -1).getTime() / 1000;
        Long endTime = new Date().getTime() / 1000;
        if (CollectionUtils.isNotEmpty(list) && list.size() > 1) {
            startTime = list.get(0);
            endTime = list.get(1);
        }
        historyParam.setStartDate(startTime);
        historyParam.setEndDate(endTime);
        if (CollectionUtils.isNotEmpty(param.getItemInfos())) {
            List<String> itemIds = param.getItemIdsDropInfos();
            for (ItemDropInfo itemDropInfo : param.getItemInfos()) {
                if (itemDropInfo.getValue_type() != null) {
                    historyParam.setValueType(itemDropInfo.getValue_type());
                    break;
                }
            }
            historyParam.setItemIds(itemIds);
        }
        return historyParam;
    }

    public static MwCoordinateDataInfo extremumHandle(ParamContext paramContext) {
        MwCoordinateDataInfo dataDto = new MwCoordinateDataInfo();
        String delay = paramContext.getDelay();
        //基础单位
        String baseUnits = paramContext.getBaseUnit();
        //监控数据间隔
        int integerDelay = getIntegerDelay(delay);
        DateTypeEnum dateTypeEnum = DateTypeEnum.getDateTypeEnum(paramContext.getDateType());
        //是否处理极值
        boolean isHandleExtremum = true;
        //极值计算，颗粒度处理
        Integer defaultDelay = 15;//默认时间间隔15分钟
        Integer period = 2;//默认截取的每个数组长度为2
        switch (dateTypeEnum) {//3:week 4:month
            case hours:
            case twelveHours:
            case days://一小时，12小时，一天不处理极值，数据直接放入avg中返回
                isHandleExtremum = false;
                break;
            case weeks:
                period = delayCount(defaultDelay, integerDelay);
                break;
            case months:
                defaultDelay = 60;//时间间隔60分钟
                period = delayCount(defaultDelay, integerDelay);
                break;
            default:
                break;
        }
        List<List<MWItemHistoryDTO>> subList = splitList(paramContext.getListDto(), period);

        List<MWItemHistoryDTO> dtoList = new ArrayList<>();
        MWItemHistoryDTO dto = new MWItemHistoryDTO();
        if (isHandleExtremum) {
            for (List<MWItemHistoryDTO> list : subList) {
                if (avgType.equals(paramContext.getExtremumType())) {
                    dto = new MWItemHistoryDTO();
                    Double avgVal = list.stream().mapToDouble(s -> doubleValueConvert(s.getValue())).average().getAsDouble();
                    dto.setValue(strValueConvert(avgVal));
                    //取数值最后一条数据的时间。
                    dto.setClock(list.get((list.size() - 1)).getClock());
                } else if (maxType.equals(paramContext.getExtremumType())) {
                    dto = list.stream().max(Comparator.comparing(s -> doubleValueConvert(s.getValue()))).get();
                } else {
                    dto = list.stream().min(Comparator.comparing(s -> doubleValueConvert(s.getValue()))).get();
                }
                dtoList.add(dto);
            }
        } else {
            //一小时，一天的数据处理
            dtoList = paramContext.getListDto();
        }
        //极值获取
        dataDto = getExtremumDTOInfo(dtoList, paramContext.convertUnit, baseUnits);
        return dataDto;
    }


    /**
     * 极值处理
     *
     * @param listDto
     */
    public static void extremumHandleTest(MwItemHistoryResult historyResult, List<MWItemHistoryDTO> listDto, Integer dateType, ItemDropInfo itemDropInfo, String convertUnit) {
        String delay = itemDropInfo.getDelay();
        //基础单位
        String baseUnits = itemDropInfo.getUnits();
        //监控数据间隔
        int integerDelay = getIntegerDelay(delay);
        DateTypeEnum dateTypeEnum = DateTypeEnum.getDateTypeEnum(dateType);
        //极值计算，颗粒度处理
        Integer defaultDelay = 15;//默认时间间隔15分钟
        Integer period = 2;//默认截取的每个数组长度为2
        switch (dateTypeEnum) {//3:week 4:month
            case weeks:
                period = delayCount(defaultDelay, integerDelay);
                break;
            case months:
                defaultDelay = 60;//时间间隔60分钟
                period = delayCount(defaultDelay, integerDelay);
                break;
            default:
                break;
        }
        List<List<MWItemHistoryDTO>> subList = splitList(listDto, period);

        List<MWItemHistoryDTO> maxList = new ArrayList<>();
        List<MWItemHistoryDTO> minList = new ArrayList<>();
        List<MWItemHistoryDTO> avgList = new ArrayList<>();
        for (List<MWItemHistoryDTO> list : subList) {
            MWItemHistoryDTO maxDto = list.stream().max(Comparator.comparing(s -> doubleValueConvert(s.getValue()))).get();
            MWItemHistoryDTO mixDto = list.stream().min(Comparator.comparing(s -> doubleValueConvert(s.getValue()))).get();
            MWItemHistoryDTO avgDto = new MWItemHistoryDTO();
            Double avgVal = list.stream().mapToDouble(s -> doubleValueConvert(s.getValue())).average().getAsDouble();
            avgDto.setValue(strValueConvert(avgVal));
            //取数值最后一条数据的时间。
            avgDto.setClock(list.get((list.size() - 1)).getClock());
            maxList.add(maxDto);
            minList.add(mixDto);
            avgList.add(avgDto);
        }
        //最大值获取
        MwCoordinateDataInfo maxDto = getExtremumDTOInfo(maxList, convertUnit, baseUnits);
        historyResult.setMaxData(maxDto);
        //最小值获取
        MwCoordinateDataInfo minDto = getExtremumDTOInfo(minList, convertUnit, baseUnits);
        historyResult.setMinData(minDto);
        //平均值获取
        MwCoordinateDataInfo avgDto = getExtremumDTOInfo(avgList, convertUnit, baseUnits);
        historyResult.setAvgData(avgDto);
    }

    public static MwCoordinateDataInfo getExtremumDTOInfo(List<MWItemHistoryDTO> list, String convertUnit, String baseUnits) {
        MwCoordinateDataInfo dto = new MwCoordinateDataInfo();
        List<String> XData = list.stream().map(s -> s.getDateConvert()).collect(Collectors.toList());
        List<String> YData = list.stream().map(s -> s.getConvertVal(convertUnit, baseUnits)).collect(Collectors.toList());
        dto.setXData(XData);
        dto.setYData(YData);
        dto.setUnit(convertUnit);
        return dto;
    }


    /**
     * 颗粒度计算
     *
     * @param defaultDelay 每defaultDelay分钟为一时间段
     * @param integerDelay 监控数据时间间隔
     * @return period 每组数据的长度
     */
    public static int delayCount(Integer defaultDelay, Integer integerDelay) {
        int period = 2;
        if (integerDelay != 0) {
            period = defaultDelay * 60 / integerDelay;
            if (period < 1) {
                period = 2;
            }
        }
        return period;
    }

    public static List<List<MWItemHistoryDTO>> splitList(List<MWItemHistoryDTO> messagesList, int groupSize) {
        int length = messagesList.size();
        // 计算可以分成多少组
        int num = (length + groupSize - 1) / groupSize; // TODO
        List<List<MWItemHistoryDTO>> newList = new ArrayList<>(num);
        for (int i = 0; i < num; i++) {
            // 开始位置
            int fromIndex = i * groupSize;
            // 结束位置
            int toIndex = (i + 1) * groupSize < length ? (i + 1) * groupSize : length;
            newList.add(messagesList.subList(fromIndex, toIndex));
        }
        return newList;
    }

    public static void main(String[] args) {
        getIntegerDelay(null);
    }

    public static int getIntegerDelay(String str) {
        int i = 0;
        String reg = "[^0-9]";
        Pattern pattern = Pattern.compile(reg);
        if(Strings.isNullOrEmpty(str)){
            return defaultDelay;
        }
        Matcher m = pattern.matcher(str);
        String trim = m.replaceAll("").trim();
        String units = str.replaceAll("\\s*", "").replaceAll("[^(A-Za-z)]", "");
        if (ZabbixItemUnitEnum.h.name().equals(units)) {
            i = Integer.valueOf(trim) * 60 * 60;
        }
        if (ZabbixItemUnitEnum.m.name().equals(units)) {
            i = Integer.valueOf(trim) * 60;
        }
        if (ZabbixItemUnitEnum.s.name().equals(units)) {
            i = Integer.valueOf(trim);
        }
        return i;
    }

    private static MwCoordinateDataInfo setDateList(List<MWItemHistoryDTO> histortList, String type, String baseUnits) {
        MwCoordinateDataInfo dto = new MwCoordinateDataInfo();
        List<String> XData = histortList.stream().map(s -> s.getDateConvert()).collect(Collectors.toList());
        //根据平均值数据的平均数据，获取合适单位
        List<Map> maps = objectsToMaps(histortList);
        Double val = maps.stream().mapToDouble(s -> doubleValueConvert(s.get(type))).average().getAsDouble();
        //单位转换
        UnitResult result = UnitUtil.getConvertedValue(new BigDecimal(val), baseUnits);
        //平均值的合适单位
        String convertAvgUnit = result.getUnits();
        List<String> YData = maps.stream().map(s -> getMatchConvertVal(strValueConvert(s.get(type)), convertAvgUnit, baseUnits)).collect(Collectors.toList());
        dto.setXData(XData);
        dto.setYData(YData);
        dto.setUnit(convertAvgUnit);
        return dto;
    }

    private static MwCoordinateDataInfo setDateListByType(List<MWItemHistoryDTO> histortList) {
        MwCoordinateDataInfo dto = new MwCoordinateDataInfo();
        List<String> XData = histortList.stream().map(s -> s.getDateConvert()).collect(Collectors.toList());
        List<String> YData = histortList.stream().map(s -> s.getValue()).collect(Collectors.toList());
        dto.setXData(XData);
        dto.setYData(YData);
        return dto;
    }

    public static String getMatchConvertVal(String value, String convertUnit, String baseUnits) {
        String convertVal = "";
        if (!Strings.isNullOrEmpty(value)) {
            convertVal = UnitUtil.getValueMap(value, convertUnit, baseUnits).getValue();
        }
        return convertVal;
    }

    public static Map<String, ItemDropInfo> setItemInfoToDisDevName(Map<String, ItemDropInfo> itemInfoByMap, Map<String, ItemNameParam> collect) {
        if (collect == null || collect.size() == 0) {
            return itemInfoByMap;
        }
        Map<String, ItemDropInfo> map = new HashMap<>();
        //设置监控项分区信息
        getItemInfoMap(map,itemInfoByMap,collect);
        return map;
    }

    public static Map<String, ItemDropInfo> setItemInfoToDevName(Map<String, ItemDropInfo> itemInfoByMap, Map<String, ItemNameParam> collect) {
        //设置监控项分区信息
        getItemInfoMap(new HashMap<>(),itemInfoByMap,collect);
        return itemInfoByMap;
    }

    private static void getItemInfoMap(Map<String, ItemDropInfo> map, Map<String, ItemDropInfo> itemInfoByMap, Map<String, ItemNameParam> collect){
        for (Map.Entry<String, ItemDropInfo> entry : itemInfoByMap.entrySet()) {
            String k = entry.getKey();
            ItemDropInfo v = entry.getValue();
            ItemDropInfo v1 = new ItemDropInfo();
            BeanUtil.copyProperties(v, v1, CopyOptions.create().setIgnoreNullValue(true));
            if (collect != null && collect.containsKey(k)) {
                ItemNameParam itemNameParam = collect.get(k);
                v1.setDevName(itemNameParam.getDevName());
                map.put(k, v1);
            }
            itemInfoByMap.put(k, v1);
        }
    }






    private static class ParamContext {
        private List<MWItemHistoryDTO> listDto;
        private Integer dateType;
        //转换后的单位
        private String convertUnit;
        //基础单位
        private String baseUnit;
        private String delay;

        //极值类型：最大值，最小值，平均值
        private String extremumType;

        public List<MWItemHistoryDTO> getListDto() {
            return listDto;
        }

        public void setListDto(List<MWItemHistoryDTO> listDto) {
            this.listDto = listDto;
        }

        public Integer getDateType() {
            return dateType;
        }

        public void setDateType(Integer dateType) {
            this.dateType = dateType;
        }

        public String getConvertUnit() {
            return convertUnit;
        }

        public void setConvertUnit(String convertUnit) {
            this.convertUnit = convertUnit;
        }

        public String getDelay() {
            return delay;
        }

        public void setDelay(String delay) {
            this.delay = delay;
        }

        public String getBaseUnit() {
            return baseUnit;
        }

        public void setBaseUnit(String baseUnit) {
            this.baseUnit = baseUnit;
        }

        public String getExtremumType() {
            return extremumType;
        }

        public void setExtremumType(String extremumType) {
            this.extremumType = extremumType;
        }
    }
}
